// MIT License (MIT)
// 
// Copyright (c) 2014 K Ercan Turkarslan
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// 

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using CodeGen;
using CollectionsLibrary;

namespace MiosGenerator
{
    public class ServiceFile : MiosFile
    {
        public ItemsCollection<StateVariable> StateVariables { get; private set; }
        public ItemsCollection<Action> Actions { get; private set; }

        #region Generate Method

        private void GenerateSetVariableMethod(CodeBlock cb)
        {
            cb.Method_Begin("internal override", "SetVariable", "String name, String value", "Boolean");

                cb.Switch_Begin("name");

                    foreach (var stateVariable in StateVariables.SortedItems)
                    {
                        cb.Switch_Case(stateVariable.Name);
                        cb.InsertIndented(
                                String.Format("_m{0} = FromLuup{1}(value); return true;",
                                    stateVariable.Name,
                                    stateVariable.DataType.CSharp));
                    }

                    cb.Switch_Default();
                    cb.Switch_Case_Return("false");
                cb.Switch_End();

            cb.Method_End("SetVariable");
        }

        internal void Generate(String generateFolder)
        {
            var cb = CodeBlock.NewCodeFile("K Ercan Turkarslan", "2014");

            cb.Using("System");
            cb.NewLine();
            cb.Namespace_Begin("Mios.Generated.ServiceFiles");

                cb.Comment(String.Format("Generated from {0}", Filename));

                cb.ChildClass(String.Format("{0}Service", ClassName), "MiosService");

                    if (StateVariables.Items.Any())
                    {
                        foreach (var stateVariable in StateVariables.Items)
                        {
                            stateVariable.Generate(cb);
                        }
                        cb.NewLine();
                    }

                    if (Actions.Items.Any())
                    {
                        foreach (var action in Actions.Items)
                        {
                            action.Generate(cb, StateVariables);
                        }
                        cb.NewLine();
                    }

                    GenerateSetVariableMethod(cb);

                    cb.Constructor_Begin(
                        "public", 
                        String.Format("{0}Service", ClassName), 
                        "MiosEngine engine, UInt32 deviceId, String serviceId", 
                        "base(engine, deviceId, serviceId)");

                    cb.NewLine();

                    cb.Constructor_End();

                cb.Class_End();

            cb.Namespace_End();

            cb.WriteToFile(Path.Combine(generateFolder, "ServiceFiles", String.Format("S{0}.cs", ClassName)));
        }

        #endregion

        #region ParseServiceFile Method
        private void ParseServiceFile(XDocument serviceFile)
        {
            Debug.Assert(serviceFile != null, "serviceFile != null");
            Debug.Assert(serviceFile.Root != null, "serviceFile.Root != null");

            StateVariables = new ItemsCollection<StateVariable>();
            foreach (var childNode in serviceFile.Root.GetChildren("serviceStateTable")
                .Where(childNode => childNode.NodeType != System.Xml.XmlNodeType.Comment))
            {
                StateVariables.Add(new StateVariable(childNode), false);
            }

            Actions = new ItemsCollection<Action>();
            foreach (var newAction in serviceFile.Root.GetChildren("actionList")
                .Where(childNode => childNode.NodeType != System.Xml.XmlNodeType.Comment)
                .Select(childNode => new Action(childNode, StateVariables)))
            {
                if (newAction.IsGetAction())
                {
                    if (newAction.FirstArgument.RelatedStateVariable.GetMethod == null) 
                    { 
                        newAction.FirstArgument.RelatedStateVariable.GetMethod = newAction;
                        continue;
                    }
                } else 
                if (newAction.IsSetAction())
                {
                    if (newAction.FirstArgument.RelatedStateVariable.SetMethod == null)
                    {
                        newAction.FirstArgument.RelatedStateVariable.SetMethod = newAction;
                        continue;
                    }
                }

                Actions.Add(newAction, false);
            }

            foreach (var stateVariable in StateVariables.SortedItems)
            {
                if ((stateVariable.SetMethod != null) || (stateVariable.Condition == null)) 
                    continue;

                // We have found a StateVariable which has no associated Set Method.
                // However this state variable defines an 'allowedValueList' or 'allowedValueRange'.
                // We will add a SetMethod just in case.

                var arguments = new ItemsCollection<ActionArgument>();
                var actionArgument = new ActionArgument(
                    String.Format("new{0}", stateVariable.Name),
                    "in",
                    false,
                    stateVariable);

                arguments.Add(actionArgument, false);

                stateVariable.SetMethod = new Action(String.Format("Set{0}", stateVariable.Name), arguments);
            }
        }
        #endregion

        #region Constructor(s)
        internal ServiceFile(String filename)
            : base(filename)
        {
            if (MiosFiles.TypeOfMiosFile(filename) != MiosFiles.Types.ServiceFile)
                throw new InvalidOperationException(
                    String.Format("[{0}] is not a Service File! Expecting S_*.xml", FullFilename));

            try
            {
                ParseServiceFile(XDocument.Load(FullFilename));
            }
            catch (System.Xml.XmlException)
            {
                throw new System.Xml.XmlException("Could not parse Xml file!");
            }
        }
        #endregion
    }
}